package com.kanan.vueupload.controller;

import com.kanan.vueupload.config.RedisUtils;
import com.kanan.vueupload.constatnt.Constant;
import com.kanan.vueupload.entity.MultipartFileParamEntity;
import com.kanan.vueupload.service.IResponseWithJson;
import com.kanan.vueupload.utils.MultipartFileUploadUtils;
import com.kanan.vueupload.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;


/**
 * @author kananan
 * @date 2021/1/17
 */
@Slf4j
@RequestMapping("/upload")
@RestController
public class UploadController implements IResponseWithJson {

    @Value("${web.upload.url}")
    private String uploadUrl;

    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private MultipartFileUploadUtils multipartFileUploadUtils;

    private final ReentrantLock reentrantLock = new ReentrantLock();

//    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @PostMapping
    public Result upload(MultipartFileParamEntity multipartFileParam,  HttpServletResponse response) throws Exception {
        log.info("进来了");
        String md5 = multipartFileParam.getIdentifier();
        String fileDirName = uploadUrl + File.separator + md5+Constant.TEMP_SUFFIX;
        //禁用FileInfo.exists()类, 防止缓存导致并发问题
        File tempFile = new File(fileDirName);
        reentrantLock.lock();//上锁
        try {
            if (!(tempFile.exists() && tempFile.isFile())) {
                if (!(tempFile.exists() && tempFile.isFile())) {
                    //创建RandomAccessFile文件
                    multipartFileUploadUtils.readySpaceFile(multipartFileParam, tempFile);
                }
            }
        }finally {
            reentrantLock.unlock();//释放锁
        }
        try {
            //写入文件
            multipartFileUploadUtils.spaceFileWriter(multipartFileParam.getFile(), tempFile, multipartFileParam);
        }catch (Exception e){
            //会出现分片参数异常 或者其他异常
            responseWithJson(response,HttpStatus.INTERNAL_SERVER_ERROR.value(),Result.error());
            return Result.error(e.getMessage());
        }

        Long size = redisUtils.getForSetSize(Constant.CHUNK_KEY + multipartFileParam.getIdentifier());
        //长度和分片总数量一致 则默认进行合并文件 切是自动合并才会默认执行合并方法 默认是自动合并 之后可以自行抽取到配置文件中
        if(multipartFileParam.getTotalChunks().equals(size) && multipartFileParam.getAutoMerge()){
            //调用合并方法
            this.multipartFileMergingChunks(multipartFileParam);

        }
        return Result.success();
    }

    /**
     * 获取已上传的分片数据
     * @param multipartFileParam
     * @return
     */
    @GetMapping
    public Result getUploadedData(MultipartFileParamEntity multipartFileParam){
        String md5 = multipartFileParam.getIdentifier();
        Object path = redisUtils.get(md5);
        List<Object> collect = null;
        if(path!=null){
            return new Result("根据MD5获取文件",true,path);
        }else {
            Set<Object> forSet = redisUtils.getForSet(Constant.CHUNK_KEY + md5);
            collect = new ArrayList<>(forSet);
        }
        return new Result("获取已上传分片信息成功",collect);
    }

    /**
     * 分片合并
     * @return {@link Result}
     */
    @RequestMapping("/multipartMerge")
    public Result multipartMerge(MultipartFileParamEntity multipartFileParam){
        try {
            this.multipartFileMergingChunks(multipartFileParam);
        }catch (Exception e){
            log.error(e.getMessage());
            return Result.error("合并失败");
        }
        return Result.success("合并成功");
    }

    /**
     * 合并文件
     * @param multipartFileParam 文件对象
     * @throws Exception
     */
    private void multipartFileMergingChunks(MultipartFileParamEntity multipartFileParam) throws Exception {
        String fileName = multipartFileParam.getFilename();
        //用来做文件名唯一性
        String md5 = multipartFileParam.getIdentifier();
        //拿到文件后缀
        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
        //拼接缓存文件夹地址
        String fileDirName = uploadUrl + File.separator + md5 + Constant.TEMP_SUFFIX;

        //拿到缓存文件
        File tempFile = new File(fileDirName);
        if (tempFile.exists() && tempFile.isFile()) {
            //文件存储路径
            String targetDirName = uploadUrl + File.separator + md5;
            //文件绝对地址
            String absolutePath = targetDirName + fileSuffix;
            //获取到目标文件
            File targetFile = new File(absolutePath);
            while (targetFile.exists() && targetFile.isFile()) {
                targetDirName = targetDirName + "1";
                targetFile = new File(targetDirName + fileSuffix);
            }
            log.info(targetFile.getAbsolutePath());
            //移动目标文件到存储路径下
            if (tempFile.renameTo(targetFile)) {
                log.info("文件重命名成功");
                redisUtils.delete(Constant.CHUNK_KEY+multipartFileParam.getIdentifier());
                //存储文件信息 这里我暂时存储到redis中 后续自行结合数据库逻辑
                //md5 作为key 文件地址
                redisUtils.set(md5,absolutePath);
            }else {
                log.error("临时文件重命名失败");
                throw new Exception("临时文件重命名失败");
            }

        }
    }


}
